﻿using System;
using System.Collections;
//using System.Linq;
using System.Xml;
using System.IO;
using System.Data;
using System.Data.SQLite;
using System.Threading;
using System.Net;
using System.Web;
using WorkQueue;

namespace DMirror
{
    public class DBWorker
    {
        private SQLiteConnection con;
        public bool Inserted
        {
            get { return inserted; }
        }
        private bool inserted = false;
        private bool hasfailed = false;
        public bool HasFailed { get { return hasfailed; } }
        public void getStatus()
        { using (DataTable dt = new DataTable()){
          using ( SQLiteDataAdapter da = new SQLiteDataAdapter(@"
select count(*) from posts where done = '1' 
union all
select count(*) from posts",con)){
              da.Fill(dt);
              int.TryParse(dt.Rows[0][0].ToString(), out DonePic);
              int.TryParse(dt.Rows[1][0].ToString(), out TotalPic);
                }}
            
        }

        public static void touchDirectory(string pathToSave)
        {
            if (!Directory.Exists(pathToSave))
            {
                Directory.CreateDirectory(pathToSave);
            }

        }
        private string spath;
        public DBWorker(string site,string path)
        {
            SQLiteConnectionStringBuilder sb = new SQLiteConnectionStringBuilder();
            sb.DataSource = site;
            spath = path;
            sb.FailIfMissing = false;
            con = new SQLiteConnection(sb.ConnectionString);
            sb = null;
            con.Open();


            try//initialize basic structure
            {
                using (SQLiteCommand cmd = con.CreateCommand())
                {
                    cmd.CommandText = @"
CREATE TABLE posts
(
id INTEGER PRIMARY KEY,
url TEXT,
md5 TEXT,
width number,
height number,
log TEXT,
done INTEGER
)";
                    cmd.ExecuteNonQuery();
                    cmd.CommandText = @";
CREATE INDEX done_idx  ON posts(id,done)
";
                    cmd.ExecuteNonQuery();
                }
            }
            catch (Exception ex)
            {
                //throw ex;
            }
            //cache all posts
            PostList = new Hashtable();
            using (DataTable results = new DataTable())
            {
                using (SQLiteDataAdapter da = new SQLiteDataAdapter("select * from posts", con))
                {
                    da.Fill(results);
                }
                foreach (DataRow dr in results.Rows)
                {
                    PostList.Add(dr["id"].ToString(), dr["done"].ToString());
                }
                
            }

        }
        public void newPost(int postid, string fileurl, bool isDone)
        {
            if (PostList.ContainsKey(postid.ToString())) return;

            using (SQLiteCommand cmd = con.CreateCommand())
            {
                string k = "0"; if (isDone) k = "1";
                cmd.CommandText = string.Format(@"insert into posts (id,url,done) values('{0}','{1}','{2}')", postid.ToString(), fileurl, k);
                cmd.ExecuteNonQuery();
                inserted = true;
               // if (lastPostID < postid) lastPostID = postid;
                
            }

        }
        public void newPost(int postid, string fileurl,string md5, bool isDone)
        {
            if (PostList.ContainsKey(postid.ToString())) return;

            using (SQLiteCommand cmd = con.CreateCommand())
            {
                string k = "0"; if (isDone) k = "1";
                
                cmd.CommandText = string.Format(@"insert into posts (id,url,md5,done) values('{0}','{1}','{2}','{3}')",
                 new string[]{
                    postid.ToString(),
                    fileurl, 
                    md5,
                    k});
                cmd.ExecuteNonQuery();
                TotalPic++;
                inserted = true;
            }

        }
        public int newPost(int postid, string fileurl, string md5,int height,int width, bool isDone)
        {
            if (PostList.ContainsKey(postid.ToString())) return 0;
           
            using (SQLiteCommand cmd = con.CreateCommand())
            {
                string k = "0"; if (isDone) k = "1";

                cmd.CommandText = string.Format(@"insert into posts (id,url,md5,height,width,done) values('{0}','{1}','{2}','{3}','{4}','{5}')",
                 new string[]{
                    postid.ToString(),
                    fileurl, 
                    md5,
                    height.ToString(),
                    width.ToString(),
                    k});
                cmd.ExecuteNonQuery();
                
            }
            TotalPic++;
            return 1;

        }
        public void setPostDone(int postid,string log)
        {
            if (!PostList.ContainsKey(postid.ToString())) return;

            using (SQLiteCommand cmd = con.CreateCommand())
            {
                cmd.CommandText = string.Format(@"update posts set done='1' where id='{0}'",
                    postid.ToString());
                cmd.ExecuteNonQuery();
                cmd.CommandText = string.Format(@"update posts set log='{1}' where id='{0}'",
                   postid.ToString(),log);
                cmd.ExecuteNonQuery();
                PostList[postid.ToString()] = "1";
                DonePic++;
            }
        }
        public void setPostDone(string postid, string log)
        {
            int k = 0;
            int.TryParse(postid, out k);
            setPostDone(k, log);
        }
        public void getShopList(int LSTCount)
        {
            Hashtable shoplists = new Hashtable();
            Hashtable shopfiles = new Hashtable();
            DataTable toget = this.toDoPosts;
            FileStream fs;StreamWriter sw;
            string url, ext;
            for (int i = 0; i < LSTCount; i++)
            {
                fs = new FileStream(string.Format("{0}\\{1}_{2}.lst",
                    spath, con.DataSource, i.ToString()
                    ), FileMode.Create);
                shopfiles.Add(i,fs );
                shoplists.Add(i, new StreamWriter(fs));
            }
            for (int i = 0; i < toget.Rows.Count; i++)
            {
                sw = (StreamWriter)shoplists[i % LSTCount];
                url = toget.Rows[i]["url"].ToString();
                ext = Path.GetExtension(url);
                url = url.Substring(0, url.LastIndexOf("/"));
                sw.WriteLine(string.Format("{0}/{1}{2}",
                    url,
                    toget.Rows[i]["id"].ToString(),
                    ext
                    ));
            }

            for (int i = 0; i < LSTCount; i++)
            {
                sw = (StreamWriter)shoplists[i];
                sw.Close();
                fs = (FileStream)shopfiles[i];
                fs.Close();
            }




            //FileStream fs = new FileStream(Path.GetDirectoryName(Application.ExecutablePath) + "\\" + txtSite.Text + ".lst", FileMode.Create);
            //StreamWriter sw = new StreamWriter(fs);
            /*
            foreach (DataRow item in d.toDoPosts.Rows)
            {
                sw.WriteLine(item["url"].ToString());
            }
            fs.Flush();

            sw.Close();
            fs.Close();
            */

        }
        public void setPostFail(int postid,string log)
        {
            if (!PostList.ContainsKey(postid.ToString())) return;

            using (SQLiteCommand cmd = con.CreateCommand())
            {
                cmd.CommandText = string.Format(@"update posts set done='2' where id='{0}'",
                    postid.ToString());
                cmd.ExecuteNonQuery();
                cmd.CommandText = string.Format(@"update posts set log='{1}' where id='{0}'",
                   postid.ToString(),log);
                cmd.ExecuteNonQuery();
                PostList[postid.ToString()] = "2";
                hasfailed = true;
            }
        }
        public DataTable allPosts
        {
            get
            {
                DataTable results = new DataTable();
                //string path = (spath+"\\"+con.DataSource+"\\");
                using (SQLiteDataAdapter da = new SQLiteDataAdapter("select * from posts order by id desc", con))
                {
                    da.Fill(results);
                   


                    TotalPic = results.Rows.Count;


                }
                return results;
            }
        }
        public void SyncFiles()
        {
            using (DataTable results = new DataTable())
            {
                string path = spath + "\\" + con.DataSource + "\\";
                using (SQLiteDataAdapter da = new SQLiteDataAdapter("select * from posts where done <> '1' ", con))
                {
                    da.Fill(results);
                    string fn;
                    string fn2;
                    foreach (DataRow item in results.Rows)
                    {
                        fn = path + item["id"].ToString() + Path.GetExtension(item["url"].ToString());
                        fn2 = path + Path.GetFileName( HttpUtility.UrlDecode(item["url"].ToString(),System.Text.Encoding.Default));
                        fn2=fn2.Replace("2c=","");

                        if (File.Exists(fn))
                        {
                            setPostDone(item["id"].ToString(), "exits");
                            //results.Rows.Remove(item);
                        }
                        if(File.Exists(fn2))
                        {
                            FileInfo i = new FileInfo(fn2);
                            i.MoveTo(fn);
                            setPostDone(item["id"].ToString(), "imported");
                        }
                    }
                  //  DonePic = TotalPic - results.Rows.Count;
                    results.Clear();
            }
        }}
        public void SyncFile()
        {
            Hashtable match1 = new Hashtable();
            string id;
            string path = spath + "\\" + con.DataSource + "\\";
            DataTable tocheck = this.toDoPosts;
            foreach (DataRow item in tocheck.Rows)
            {
                match1.Add(path + item["id"].ToString() + Path.GetExtension(item["url"].ToString()), item["id"].ToString());  
            }
          
            //get file list
            DirectoryInfo di = new DirectoryInfo(path);
            FileSystemInfo[] files = di.GetFileSystemInfos();
            foreach (FileSystemInfo item in files){
                if (match1.ContainsKey(item.FullName))
                {
                    id = (string)match1[item.FullName];
                    setPostDone(id, "exits");
                }}
            files = null;
        }
        public DataTable toDoPosts
        {
            get
            {
                DataTable results = new DataTable();
                using (SQLiteDataAdapter da = new SQLiteDataAdapter("select * from posts where done <> '1' ", con))
                {
                    da.Fill(results);
                }
                return results;
            }
        }
        private Hashtable PostList;
        public int TotalPic = 0;
        public int DonePic = 0;
      
    }
    public class XMLWorker
    {
        private XmlDocument doc;

        public XMLWorker(string fileName,ref DBWorker d)
        {
            try{
            doc = new XmlDocument();
            FileStream fs = new FileStream(fileName, FileMode.Open);
            doc.Load(fs);
            XmlNode posts = doc.DocumentElement.SelectSingleNode("/posts");
                int total = 0;
                int offset = 0;
                int height = 0;
                int width = 0;
                //int.TryParse(posts.Attributes["count"].Value, out total);
                //int.TryParse(posts.Attributes["offset"].Value, out offset);
                //toContinue = (total > offset);
                foreach (XmlNode item in posts.SelectNodes("/posts/post"))
                {
                    total++;
                    int.TryParse(item.Attributes["id"].Value, out total);
                    int.TryParse(item.Attributes["height"].Value, out height);
                    int.TryParse(item.Attributes["width"].Value, out width);
                    offset+=d.newPost(total, item.Attributes["file_url"].Value,
                    item.Attributes["md5"].Value, height, width, false);
                }
                fs.Close();
                fs.Dispose();
                
                //if (offset == 0) toContinue = false; else toContinue = true;
            }
            catch (Exception e)
            {
                //toContinue = false;
            }
        }

        public XMLWorker(string site, int page, int limit,ref DBWorker d)
        {
            string siteurl = "http://{0}/post/index.xml?limit={1}&page={2}";
            try
            {
                doc = new XmlDocument();
                HttpWebRequest r = (HttpWebRequest)WebRequest.Create(string.Format(siteurl, site, limit.ToString(), page.ToString()));
                HttpWebResponse p = (HttpWebResponse)r.GetResponse();
                Stream xmlstream = p.GetResponseStream();
                doc.Load(xmlstream);
                XmlNode posts = doc.DocumentElement.SelectSingleNode("/posts");
                int total = 0;
                int offset = 0;
                int height = 0;
                int width = 0;
                //int.TryParse(posts.Attributes["count"].Value, out total);
                //int.TryParse(posts.Attributes["offset"].Value, out offset);
                //toContinue = (total > offset);
                foreach (XmlNode item in posts.SelectNodes("/posts/post"))
                {
                    total++;
                    int.TryParse(item.Attributes["id"].Value, out total);
                    int.TryParse(item.Attributes["height"].Value, out height);
                    int.TryParse(item.Attributes["width"].Value, out width);
                    offset+=d.newPost(total, item.Attributes["file_url"].Value,
                    item.Attributes["md5"].Value, height, width, false);
                }
                if (offset == 0) toContinue = false; else toContinue = true;
            }
            catch (Exception e)
            {
                toContinue = false;
            }
        }
        public bool ToBeContinued
        {
            get { return toContinue ; }
        }
        private bool toContinue = false;
    }
    public class HTTPPool
    {
        private static string _pathToSave;
        private int _threadNum;
        private static DBWorker db;
        private static DataTable _fileToDownload;
        public static DataTable downloadStatus;
        private WorkQueue<DataRow> que;
        public HTTPPool(int threadNumber, ref DBWorker d, string storePath)
        {
            _fileToDownload = d.toDoPosts;
            touchDirectory(storePath);
            _pathToSave = storePath;
            _threadNum = threadNumber;
            db = d;
            //multi thread download
            que = new WorkQueue<DataRow>(_threadNum);
            que.UserWork += new UserWorkEventHandler<DataRow>(workThread);
            ThreadPool.QueueUserWorkItem(o =>
                {
                    for(int i=0;i<_fileToDownload.Rows.Count;i ++)
                    {
                        que.EnqueueItem(_fileToDownload.Rows[i]);
                    }
                });

        } 
        public static void touchDirectory(string pathToSave)
        {
            if (!Directory.Exists(pathToSave))
            {
                Directory.CreateDirectory(pathToSave);
            }

        }
        public static MemoryStream getSingleFile(string url)
        {
            MemoryStream m = new MemoryStream();
            HttpWebRequest r = (HttpWebRequest)WebRequest.Create(url);
            HttpWebResponse p = (HttpWebResponse)r.GetResponse();
            Stream re = p.GetResponseStream();
            byte[] buffer = new byte[8192];
            int cnt = 0; int offset = 0;
            do
            {
                cnt = re.Read(buffer, 0, buffer.Length);
                m.Write(buffer, 0, cnt);
                offset += cnt;
            } while (cnt > 0);
            m.Flush();
            m.Position = 0;
            return m;
        }

        static void workThread(object sender,
            WorkQueue<DataRow>.EnqueueEventArgs e)
        {
            DataRow d = e.Item;
            download(d["id"].ToString(),d["url"].ToString());
        }

        private static void download(string sid, string url)
        {
            int id = 0;
            int.TryParse(sid, out id);
            string fn;
            try
            {
                fn  = string.Format("{0}{1}{2}",
                        _pathToSave,
                        sid,
                        Path.GetExtension(url));
                if (File.Exists(fn))
                {
                    db.setPostDone(id,"exists");
                    return;
                }
                using (MemoryStream s = getSingleFile(url))
                {  
                    using (FileStream fs = File.Create(string.Format("{0}\\{1}{2}",
                        _pathToSave,
                        sid,
                        Path.GetExtension(url)
                        ), (int)s.Length))
                    {
                        s.WriteTo(fs);
                        fs.Flush();
                        db.setPostDone(id,"get");
                        Thread.Sleep(15);
                    }
                }
            }
            catch (Exception e)
            {
                //throw e;
                db.setPostFail(id,e.Message);
                Thread.Sleep(15);
            }


        }
    }
}
